#include <am.h>
#include <klib.h>
#include <klib-macros.h>
#include <stdarg.h>
#include <stdio.h>

#if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__)

void print(const char *str, char flags, int width, char specifier) {
  switch (specifier) {
    case 's': {
      int is = 0;
      while (str[is] != '\0') {
        putch(str[is++]);
      }
      break;
    }

    case 'd': {
      // output padding
      if ((flags == '0' || flags == ' ') && width > 0) {
        for (int j = 0; j < width - strlen(str); j ++) {
          putch(flags);
        }
      }

      int id = 0;
      while (str[id] != '\0') {
        putch(str[id++]);
      }

      break;
    }

    case 'x': {
      if (flags == '#') {
        putch('0');
        putch('x');
      }

      int ix = 0;
      while (str[ix] != '\0') {
        putch(str[ix++]);
      }

      break;
    }

    case 'u': {
      // output padding
      if ((flags == '0' || flags == ' ') && width > 0) {
        for (int j = 0; j < width - strlen(str); j ++) {
          putch(flags);
        }
      }

      int iu = 0;
      while (str[iu] != '\0') {
        putch(str[iu++]);
      }

      break;
    }

    default: {
      int i = 0;
      char *s = "NO implemented\n";
      while(s[i] != '\0') {
        putch(s[i++]);
      }
      // exit(1);
    }
  }
}

int parseArgs(const char *str, char *flags, int *width, char *length, char *specifier) {
  *width = 0;
  *flags = '\0';
  *length = '\0';
  *specifier = '\0';

  int i = 0;
  while (str[i] != 's' && str[i] != 'd' && str[i] != 'x' && str[i] != 'u') {
    if (str[i] == '-' || str[i] == '0' || str[i] == '#')
      *flags = str[i];
    else if (str[i] > '0' && str[i] <= '9') {
      *width = *width * 10 + str[i] - '0';
    }
    else if (str[i] == 'l') {
      *length = str[i];
    }

    i ++;
  }

  *specifier = str[i];

  return i;
}

int printf(const char *fmt, ...) {
  int count = 0;
  char flags, length, specifier;
  int width;

  va_list ap;

  va_start(ap, fmt);

  int ptr_fmt;
  for (ptr_fmt = 0; fmt[ptr_fmt] != '\0'; ptr_fmt++) {
    if (fmt[ptr_fmt] == '%') {
      flags = '\0';
      specifier = '\0';
      length = '\0';
      width = 0;

      ptr_fmt += parseArgs(&fmt[ptr_fmt], &flags, &width, &length, &specifier);

      switch (specifier) {
        case 's' : {
          char *strs = va_arg(ap, char *);
          print(strs, flags, width, specifier);
          count += strlen(strs);
          break;
        }

        case 'd' : {
          char strd[65];
          if (length == '\0') {
            int numd = va_arg(ap, int);
            sitoa(numd, strd, 10);
          }
          else if (length == 'l') {
            long numd = va_arg(ap, int);
            slitoa(numd, strd, 10);
          }
          print(strd, flags, width, specifier);
          count += strlen(strd);
          break;
        }

        case 'x' : {
          int numx = va_arg(ap, int);
          char strx[33];
          sitoa(numx, strx, 16);
          print(strx, flags, width, specifier);
          count += strlen(strx);
          break;
        }

        case 'u' : {
          int numu = va_arg(ap, unsigned int);
          char stru[33];
          uitoa(numu, stru, 10);
          print(stru, flags, width, specifier);
          count += strlen(stru);
          break;
        }

        default:
          panic("specifier is not implemented");
      }
    }
    else {
      putch(fmt[ptr_fmt]);
      count++;
    }
  }

  va_end(ap);

  return count;
}

int vsprintf(char *out, const char *fmt, va_list ap) {
  panic("Not implemented");
}

int sprintf(char *out, const char *fmt, ...) {
  va_list ap;

  va_start(ap, fmt);

  int ptr_out, ptr_fmt;
  for (ptr_out = ptr_fmt = 0; fmt[ptr_fmt] != '\0'; ptr_fmt++) {
    if (fmt[ptr_fmt] == '%') {
      ptr_fmt++;

      switch (fmt[ptr_fmt]) {
        case 's' :
          char *strs = va_arg(ap, char *);
          strcpy(out+ptr_out, strs);
          ptr_out += strlen(strs);
          break;

        case 'd' :
          int num = va_arg(ap, int);
          char strd[33];
          sitoa(num, strd, 10);
          strcpy(out+ptr_out, strd);
          ptr_out += strlen(strd);
          break;
      }
    }
    else out[ptr_out++] = fmt[ptr_fmt];
  }
  out[ptr_out] = '\0';

  va_end(ap);

  return strlen(out);
}

int snprintf(char *out, size_t n, const char *fmt, ...) {
  panic("Not implemented");
}

int vsnprintf(char *out, size_t n, const char *fmt, va_list ap) {
  panic("Not implemented");
}

#endif
